package com.bsf.mvc.controller;

import com.bsf.log.DebugLog;
import com.bsf.log.ErrorLog;
import com.bsf.mvc.util.ClassUtil;
import com.bsf.util.ExceptionUtil;
import com.bsf.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;

/**
 * 自动RequestMapping映射
 */
public class AutoRequestMappingProvider {
    RequestMappingInfoHandlerMapping requestMappingInfoHandlerMapping;

    public AutoRequestMappingProvider(RequestMappingInfoHandlerMapping requestMappingInfoHandlerMapping) {
        this.requestMappingInfoHandlerMapping = requestMappingInfoHandlerMapping;
    }

    public void init(String... packageNames) {
        List<String> ps = new ArrayList<>();
        for (String p : packageNames) {
            if (!StringUtils.isEmpty(p)&&!ps.contains(p))
                ps.add(p);
        }
        if(packageNames == null||packageNames.length==0) {
            for (Package p : Package.getPackages()) {
                if (!ps.contains(p))
                    ps.add(p.getName());
            }
        }
        List<Class<?>> classs = getControllers(ps);

        for (Class<?> c : classs) {
            List<Method> methods = getPublicMethods(c);
            for (Method m : methods) {
                String url = "";
                try {
                    url = getDefaultUrl(c, m);
                    PatternsRequestCondition patterns = new PatternsRequestCondition(url);
                    RequestMethodsRequestCondition requestmethods = new RequestMethodsRequestCondition();
                    ParamsRequestCondition params = new ParamsRequestCondition();
                    HeadersRequestCondition headers = new HeadersRequestCondition();
                    ConsumesRequestCondition consumes = new ConsumesRequestCondition();
                    ProducesRequestCondition produces = new ProducesRequestCondition();
                    RequestMappingInfo info = new RequestMappingInfo(patterns, requestmethods, params, headers, consumes, produces, null);
                    requestMappingInfoHandlerMapping.registerMapping(info, requestMappingInfoHandlerMapping.getApplicationContext().getAutowireCapableBeanFactory().createBean(c), m);
                    DebugLog.write("【自动注入】"+url,this.getClass());
                } catch (Exception exp) {
                    DebugLog.write("【警告】自动注入默认RequestMapping出错,url:" + StringUtil.nullToEmpty(url) + ",详细信息:" + ExceptionUtil.getDetailMessage(exp), this.getClass());
                }
            }
        }
    }

    private String getDefaultUrl(Class<?> cls, Method method) {
        return "/" + cls.getSimpleName().toLowerCase().replace("controller", "") + "/" + method.getName().toLowerCase();
    }

    private ArrayList<Class<?>> getControllers(List<String> packageNames) {
        ArrayList<Class<?>> cs = new ArrayList<>();
        //ArrayList<Class<?>> rs = new ArrayList<>();
        for (String p : packageNames) {
            try {
                List<String> classes = ClassUtil.getClassName(p);
                for (String cls: classes) {
                    Class<?>c = Class.forName(cls);
                    //rs.add(c);
                    if (BaseController.class.isAssignableFrom(c) && !cs.contains(c)) {
                        cs.add(c);
                    }
                }
            } catch (Throwable e) {
            }
        }

        return cs;
    }

    private ArrayList<Method> getPublicMethods(Class<?> cls) {
        ArrayList<Method> ms = new ArrayList();
        Method[] methods = cls.getDeclaredMethods();
        for (Method m : methods) {
            if (Modifier.isPublic(m.getModifiers()) && hasRequestMapping(m) == false && ModelAndView.class.isAssignableFrom(m.getReturnType())) {
                ms.add(m);
            }
        }
        return ms;
    }

    private boolean hasRequestMapping(Method method) {
        RequestMapping mapping = method.getAnnotation(RequestMapping.class);
        if (mapping == null)
            return false;
        else
            return true;
    }
}
